HDR Imaging and Display

How can those who live in the light of the day possibly comprehend the depths of the night?

--Nietzsche

Author: Zhen Tong

In this project, we refer to the 2002 paper for HDR image processing. Our contribution includes implementation:

Step 1. Merge LDR RAW into 32-bit HDR Image

Given a list of RAW image of different light exposure, putting them through the first few process of isp, Dead Pixel Correction, Black Level Compensation, Anti Aliasing Filter, and Auto White Balance and Gain Control. Then we can get on the Raw Exposure Fusion process.

Ii=f(tiX)Ilini=f1(Ii)f(x)=x1/γ

t is the exposure time, different jpg images exposure of different time ti. Since we start from RAW images which are already in linear space (without gamma function f), we don't need to estimate camera response curve any more. Because human eye percept the light like a log function, therefore, we calculate the weight of pixel xijXk in image exposure k.

wij=exp(4(xijmedian(X))2median(X)2)

Here, the weight wij is used to extract useful pixel under certain exposure time. Here we assume that the more one pixel is close to the mean value of the respond range, the more accurate and useful the pixel is. For example with tk time of exposure, the pixel at (i,j) computed with a large weight wij1 which means this pixel can be well-presented under tk, and should be fully used in the merging step.

To estimate the true intensity X in HDR, we can minimize the weighted least-squares objective function in log-space:

minXijkwijk(log(Ii,jk)log(tkXijk)2)solve by gradiant=0: X^ij=exp(kwijk(log(Ii,jk)log(tk))kwij)

Output

After we run theget_fusion_weights() function, we can get the weight for each exposure. From the following graphs, we can observe, most of the pixels are 0 weight, and the pixels with weight wij=1 are also in large amount. because the weight distribution is in "bell" shape (the pixels of wij=1 are close to the median value of the distribution).

Output Weight as Image:

From the output of the merging LDR Exposurs.png we can see the 7 given exposure domain was mapped to the HDR scale. The first 7 pictures are the weight value of each pixel wij[0,1], we can observe that the the short time exposure, like picture 0, has large weight at the dark place, while the long time exposure, like picture 7, has large weight at the bright place. This is because according to the assumption, the accurate pixel will concentrate at the middle point of the exposure time.

Step 2. Demosaic the Fused Raw Data & Save .EXR File

After the merging, using the Malvar Color Filter Array Interpolation and output the rbg image. Using OpenEXR, Imath

Output

Fuse seven pictures into one.

Step 3. Tone Mapping with Bilateral Filter

If the output HDR image needs to mapped from huge range of domain to [0,255], or else, the RGB image have the overexposure problem if output directively, and the image above is also not good enough the local information lost.

The idea of Tone Mapping is separate the the base and detail of the Y channel in YUV, and using the bilateral filter to remain the detail.

Bilateral Filter is an edge preserving denoising filter, G is a Gaussian function with mean = 0.

BF[I]p=1WpqSGσs(|pq|)Gσr(IpIq)IqWp=qSGσs(|pq|)Gσr(IpIq)

Implement BF

Using the vectorization of the numpy computation, we can speed up the computation. We can first get all the indices of the kernel computation locations using np.meshgrid, and then do the kernel computation all at once.

Output

According to the paper:

we only use a two scale decomposition, where the “base” image is computed using bilateral filtering, and the detail layer is the division of the input intensity by the base layer.

First we change the output of CFA interpolation directly from RBG into YUV and then use the Bilateral Filter to get the base, and get the detail using:

Ydetail=YYbase

Then, we can get the new Y channel with:

Ynew=fnormalize(log(Ybase)+Ydetail)

The fnormalize is the mapping function that can map the arbitrary range to the [16,235].

Apply the new Y channel data, and recompose with the UV channel, convert it into RGB, we can get the output.

Then we can go through the rest of the ISP process. (The color of the sky slightly changes)

Compared with Gamma without Tone Mapping:

Using the rest of the ISP pipeline (Edge Enhancement, Brightness/Contrast Control, False Color Suppresion, and Hue/Saturation control), we can get the final output.

output

The green color on the left is because the trees reflect green light. The yellow on the right is because the stone reflects yellow light. So hdr can save these color details after preserving the texture of the leaves

How to Run

You should have the file structure like this:

Environment

ArgsValueHelp
--draw-intermediate Draw the intermediate pictures for the intermediate stage
--set-dir../data/set03 
--output-dirdataThe final output image you want to save
--itmd-diritmdThe intermediate path for stage you want to save
--faststore trueUse the faster version of the Bilateral Filter
--kernel-size5The kernel size of the BF

Run as ipynb Notebook

For the status check, we recommend you run the notebook.ipynb for detail output image of each state.

Experiments

See the running log at Runing Log

References